﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using YXH_Tools_Files.ObjectClass;
using YXH_Tools_Files.Tools_CSV;
using YXH_Tools_Files.Tools_DirectoryandFile;
using YXH_Tools_Files.Tools_EdgeComputing;
using YXH_Tools_Files.Tools_Error;
using YXH_Tools_Files.Tools_Spike;

namespace YXH_Tools_Files.Tools_Excute
{
    public static class Tools_Excute_CSVSpikeRemoval
    {
        /// <summary>
        /// 该方法接受一个字符串列表作为输入数据，以及要替换的列号和新数据列表。
        /// 它将根据列号使用新数据替换原始数据，并直接修改原始数据。
        /// </summary>
        /// <param name="od">原数据</param>
        /// <param name="columntoreplace">需要替换的列号</param>
        /// <param name="nd">替换的数据(全部数据)</param>
        /// <exception cref="Exception">列号超出界限</exception>
        public static async Task ReplaceColumnInCSV(List<string> od, int columntoreplace, List<double> nd)
        {
           await Task.Run(() =>
            {
                //这里i要从1开始，因为od里的第0行是列名，不是数据
                for (int i = 1; i < od.Count; i++)
                {
                    // Split the line into an array of columns
                    string[] columns = od[i].Split(',');

                    // Check if the column index is within the range
                    if (columntoreplace >= 0 && columntoreplace < columns.Length)
                    {
                        // Replace the column with new data
                        columns[columntoreplace] = nd[i-1].ToString();
                    }
                    else
                    {
                        Console.WriteLine($"列号超出界限: {columntoreplace}");
                        throw new Exception($"列号超出界限: {columntoreplace}");
                    }
                    //替换原来的每一行string
                    od[i] = string.Join(",", columns);

                }
            });
            
           
        }

        /// <summary>
        /// 该方法接受一个字符串列表作为输入数据，以及要替换的列号，起始行数和新数据（部分数据）。
        /// </summary>
        /// <param name="od"></param>
        /// <param name="columntoreplace"></param>
        /// <param name="nd"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static async Task ReplaceColumnInCSVV2(List<string> od, int columntoreplace, int startrow, List<double> nd)
        {
            await Task.Run(() =>
            {
                //这里i要从1开始，因为od里的第0行是列名，不是数据
                for (int i = 1; i < od.Count; i++)
                {
                    // Split the line into an array of columns
                    string[] columns = od[i].Split(',');

                    // Check if the column index is within the range
                    if (columntoreplace >= 0 && columntoreplace < columns.Length)
                    {
                        // Replace the column with new data
                        //判断是否到了起始行并且小于最后一个，则开始替换
                        if(i>=startrow&&i<=startrow+nd.Count-1)
                        {
                            columns[columntoreplace] = nd[i - startrow].ToString();
                        }
                    }
                    else
                    {
                        Console.WriteLine($"列号超出界限: {columntoreplace}");
                        throw new Exception($"列号超出界限: {columntoreplace}");
                    }
                    //替换原来的每一行string
                    od[i] = string.Join(",", columns);

                }
            });


        }

        /// <summary>
        /// 根据列号来删除特定列号数据的spike
        /// </summary>
        /// <param name="originalpath"></param>
        /// <param name="cleanpath"></param>
        /// <param name="configuredfolders"></param>
        /// <param name="columnstoreplace"></param>
        /// <returns></returns>
        public static async Task<List<string>> YXHExcuteCsvSpike(string originalpath, string cleanpath, string[] configuredfolders, List<int> columnstoreplace, int blockLengthMean, int blockLengthVariance, double threshold,double ratio)
        {
            List<string> list = new List<string>();//专用于输出程序计算过程中的错误信息
            try
            {
                await Task.Run(async () => {

                    Directory.CreateDirectory(cleanpath);
                    //获得需要合并的源数据日期文件夹列表
                    var ds = Tools_Excute_CSVCombine.YXHGetProcessedFolders(originalpath, cleanpath, configuredfolders);
                    if (ds != null)
                    {
                        if (ds.Length > 0)
                        {
                            Stopwatch timer = new Stopwatch();
                            timer.Start();
                            foreach (var d in ds)
                            {
                                Console.WriteLine("开始删除spike文件夹名为" + d.Name + "的文件，请稍等片刻");
                                var fileinfo = BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.csv");

                                if (fileinfo.Length == 0 || fileinfo == null)
                                {
                                    Console.WriteLine("文件路径可能有错误，无法读取到csv文件！！！");
                                    list.Add($"{d.Name}文件夹下没有csv文件或文件夹路径有问题");
                                }
                                else
                                {
                                    //筛选0kb的csv的数据并转换成txt
                                    fileinfo.YXHTransferZeroCsvtoTxt();
                                    for (int i = 0; i < fileinfo.Length; i++)
                                    {
                                        //文件大小不等0则需要clean
                                        if (fileinfo[i].Length != 0)
                                        {
                                            string resultdirectorypath = Path.Combine(cleanpath, d.Name);
                                            DirectoryInfo root = new DirectoryInfo(resultdirectorypath);
                                            if (!root.Exists)
                                            {
                                                root.Create();
                                            }
                                            string resultpath = Path.Combine(root.FullName, fileinfo[i].Name);
                                            var odlist = await CSVOperator.YXHReadCSV2ListOneTime(fileinfo[i]);//读取csv数据到List<string>,每一行为一个string
                                                                                                               //读取csv多个特定列的数据到List<List<double>>>，这是需要去除spike的数据集
                                            var datalists = await CSVOperator.YXHReadColumnsfromCSV2List(fileinfo[i].FullName, columnstoreplace);

                                            for (int j = 0; j < columnstoreplace.Count; j++)
                                            {
                                                //去除spike后的数据
                                                var spikeremovelist = RemoveSpikeProcess.completeSpikeRemovalProcess(datalists[j], blockLengthMean, blockLengthVariance, threshold, ratio, fileinfo[i].Name);
                                                //把去除spike后的数据替换到原数据
                                                await ReplaceColumnInCSV(odlist, columnstoreplace[j], spikeremovelist);
                                            }
                                            //最后输出CSV                                  
                                            await CSVOperator.YXHSaveCSVfromListOneTimeAsync(resultpath, odlist);
                                        }

                                    }
                                }
                            }
                            timer.Stop();
                            Console.WriteLine($"程序总完成时间为{timer.Elapsed.TotalMinutes:0.00}分");
                            list.Add($"程序总完成时间为{timer.Elapsed.TotalMinutes.ToString("0.00")}分");
                        }
                        else
                        {
                            Console.WriteLine("目标路径下已有所有数据，不进行任何操作");
                            list.Add("目标路径下已有所有数据，不进行任何操作");
                        }
                     
                    }
                    else
                    {
                        Console.WriteLine("合并路径有问题，请检查配置文件");
                        list.Add("目标路径有问题，请检查配置文件");

                    }

                });
            }
            catch (Exception ex)
            {
                list.Add("程序出错，请查看错误日志");
                string methodName = "";
                // 获取堆栈跟踪信息
                StackTrace stackTrace = new StackTrace(ex);
                // 获取第一个堆栈帧
                StackFrame? stackFrame = stackTrace.GetFrame(0);
                // 获取方法名
                if (stackFrame != null)
                {
                    var s = stackFrame.GetMethod();
                    if (s != null)
                    {
                        methodName = s.Name;
                    }
                }
                LogErrorMessage.getErrormessage(methodName + "方法出现了错误：" + ex.StackTrace);
                Console.WriteLine(methodName + "方法出现了错误：" + ex.StackTrace);
            }
            return list;
        }
        /// <summary>
        /// 去除spike第二种方式，先利用停车合并的数据进行识别，识别出有spike的文件，然后再回到源数据路径下单独对这些文件进行处理，保存到新的路径下
        /// </summary>
        /// <param name="originalpath"></param>
        /// <param name="cleanpath"></param>
        /// <param name="configuredfolders"></param>
        /// <param name="txtcombinedpath"></param>
        /// <param name="columnstoreplace"></param>
        /// <param name="blockLengthMean"></param>
        /// <param name="blockLengthVariance"></param>
        /// <param name="threshold"></param>
        /// <param name="ratio"></param>
        /// <returns></returns>
        public static async Task<List<string>> YXHExcuteCsvSpikeV2(string originalpath, string cleanpath, string[] configuredfolders, string txtcombinedpath, List<int> columnstoreplace, int blockLengthMean, int blockLengthVariance, double threshold, double ratio)
        {
            List<string> list = new List<string>();//专用于输出程序计算过程中的错误信息
            //存放每个日期文件夹下的停车合并的文件集
            List<FileInfo[]> fileInfos_txtcombined = new List<FileInfo[]>();
            //存放每个日期文件夹下的原始文件集
            List<FileInfo[]> fileInfos_od = new List<FileInfo[]>();
            //先判断在每个日期文件夹下停车合并的文件集第一个文件名是否在原始文件集里面，如果有就执行这个文件夹下的操作，如果没有说明
            //路径选错了
            try
            {
                await Task.Run(async () => {
                    Stopwatch timer = new Stopwatch();
                    timer.Start();

                    Directory.CreateDirectory(cleanpath);
                    //获得停车合并路径下的日期文件夹列表
                    var ds_txtcombined = Tools_Excute_CSVCombine.YXHGetProcessedFolders(txtcombinedpath, cleanpath, configuredfolders);
                    //获得需要合并的源数据日期文件夹列表
                    var ds_od = Tools_Excute_CSVCombine.YXHGetProcessedFolders(originalpath, cleanpath, configuredfolders);
                    List<string> datename = new List<string>();
                    if (ds_od != null)
                    {
                        if (ds_od.Length > 0)
                        {
                            foreach (var d in ds_od)
                            {
                                fileInfos_od.Add(BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.csv"));
                                datename.Add(d.Name);
                            }
                        }
                    }
                    else
                    {
                        list.Add($"原始路径{originalpath}下没有日期文件夹");
                        return ;
                    }
                    if (ds_txtcombined != null)
                    {
                        if (ds_txtcombined.Length > 0)
                        {
                            foreach (var d in ds_txtcombined)
                            {
                                fileInfos_txtcombined.Add(BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.csv"));
                            }
                        }
                    }
                    else
                    {
                        list.Add($"停车合并路径{txtcombinedpath}下没有日期文件夹");
                        return;
                    }
                    //先判断这两个日期文件集的数量是否一直，不一致说明两边日期对不上
                    if (fileInfos_od.Count == fileInfos_txtcombined.Count)
                    {
                        //一个一个日期文件夹进行
                        for (int i = 0; i < fileInfos_txtcombined.Count; i++)
                        {
                            //筛选0kb的csv的数据并转换成txt
                            fileInfos_od[i].YXHTransferZeroCsvtoTxt();
                            string resultdirectorypath = Path.Combine(cleanpath, datename[i]);
                            Directory.CreateDirectory(resultdirectorypath);
                            //判断每个日期文件夹下面有么有csv文件
                            if (fileInfos_od[i].Length > 0 && fileInfos_txtcombined[i].Length > 0)
                            {
                                var filetxt = fileInfos_txtcombined[i][0].Name;
                                var fileod = fileInfos_od[i][0].Name.Split(".")[0];
                                //判断是否每一个日期文件夹下的第一个停车合并csv名字里包含原始的第一个csv文件名字
                                if (filetxt.Contains(fileod))
                                {
                                    //fileInfos_txtcombined[i]就是指某个日期文件夹下的所有csv文件合集
                                    foreach (var item in fileInfos_txtcombined[i])
                                    {
                                        Console.WriteLine(item.Name);
                                        var sp = item.Name.Split("-to-");
                                        //存储第一个文件名的序号
                                        int firstfileinod = -1;
                                        if (sp.Length == 2)
                                        {
                                            for (int n = 0; n < fileInfos_od[i].Length; n++)
                                            {
                                                //判断源数据里哪个文件名与停车合并后的第一个文件名一致,保存序号
                                                if (fileInfos_od[i][n].Name.Split(".")[0] == sp[0])
                                                {
                                                    firstfileinod = n; break;
                                                }
                                            }
                                        }
                                        else
                                        {
                                            list.Add($"停车合并文件{item.Name}文件名格式不对");
                                        }
                                        //一个个去读取停车合并后的csv文件数据.读取每一个csv数据到List<string>,每一行为一个string，包括标题行
                                        //读取csv多个特定列的数据到List<List<double>>>，这是需要去除spike的数据集，不包括标题行
                                        var datalists = await CSVOperator.YXHReadColumnsfromCSV2List(item.FullName, columnstoreplace);
                                        //存储处理每个停车合并文件spike的SpikeinFileClass类数据集
                                        List<SpikeinFileClass> totalonetxtcombinedfile = new List<SpikeinFileClass>();
                                        //对每一个指定列里的数据进行spike检测
                                        for (int j = 0; j < columnstoreplace.Count; j++)
                                        {
                                            await Console.Out.WriteLineAsync($"开始添加第{j}列的SpikeinFileClass");
                                            //获得去除spike后的某指定列的数据类
                                            var spikeremovelist = RemoveSpikeProcess.completeSpikeRemovalProcessV2(datalists[j], blockLengthMean, blockLengthVariance, threshold, ratio, columnstoreplace[j]);
                                            if (spikeremovelist != null)
                                            {
                                                totalonetxtcombinedfile.AddRange(spikeremovelist);
                                            }
                                            //把去除spike后的数据替换到原数据
                                        }
                                        await Console.Out.WriteLineAsync($"totalonetxtcombinedfile数量{totalonetxtcombinedfile.Count}");

                                        var groupdata = totalonetxtcombinedfile.GroupBy(s => s.spikefileindex);
                                        // 遍历每个组别并取出数据，每个组别就是相同文件名的一组SpikeinFileClass数据
                                        foreach (var group in groupdata)
                                        {
                                            //int spikefileindex = group.Key; // 获取组别的 spikefileindex
                                            var eachgroup = group.ToList(); // 获取该组别的数据列表
                                            var fileindexinod = eachgroup?.FirstOrDefault()?.spikefileindex;
                                            if (fileindexinod != null && firstfileinod != -1)
                                            {
                                                //这是实际的有spike的文件在源数据文件夹里的index位置
                                                var realindexinod = fileindexinod + firstfileinod;
                                                var odlist = await CSVOperator.YXHReadCSV2ListOneTime(fileInfos_od[i][(int)realindexinod]);
                                                if (eachgroup != null)
                                                {
                                                    
                                                    foreach (var c in eachgroup)
                                                    {
                                                        if (c.filtervalue != null)
                                                        {
                                                            //Console.WriteLine(c.);
                                                            await ReplaceColumnInCSVV2(odlist, c.spikecollumn, c.spikestartrow, c.filtervalue);
                                                        }
                                                    }
                                                }
                                                var resultpath = Path.Combine(resultdirectorypath, fileInfos_od[i][(int)realindexinod].Name);
                                                //这里的odlist是已经修改过了的数据
                                                await CSVOperator.YXHSaveCSVfromListOneTimeAsync(resultpath, odlist);
                                                //Console.WriteLine("这是需要修改的文件"+resultpath);
                                            }
                                            // 在这里可以对 groupData 进行进一步处理，例如输出或者进行其他操作
                                        }
                                    }
                                }
                                else
                                {
                                    list.Add($"停车合并路径与原文件{datename[i]}路径下第一个csv文件不一致");
                                    break;
                                }
                                ////复制其他不需要改动的csv文件
                                //foreach (var csvFile in fileInfos_od[i])
                                //{
                                //    var dp = Path.Combine(resultdirectorypath, csvFile.Name);
                                //    if(!File.Exists(dp))
                                //    File.Copy(csvFile.FullName, dp, false);

                                //}
                            }
                            else
                            {
                                list.Add($"停车合并路径或者原文件{datename[i]}路径下没有csv文件");
                                break;

                            }
                        }
                    }
                    else
                    {
                        list.Add($"停车合并路径和原始路径日期文件夹不一致");
                        return;
                    }
                    timer.Stop();
                    Console.WriteLine($"程序总完成时间为{timer.Elapsed.TotalMinutes:0.00}分");
                    list.Add($"程序总完成时间为{timer.Elapsed.TotalMinutes.ToString("0.00")}分");
                });

            }
            catch (Exception ex)
            {
                list.Add("程序出错，请查看错误日志");
                string methodName = "";
                // 获取堆栈跟踪信息
                StackTrace stackTrace = new StackTrace(ex);
                // 获取第一个堆栈帧
                StackFrame? stackFrame = stackTrace.GetFrame(0);
                // 获取方法名
                if (stackFrame != null)
                {
                    var s = stackFrame.GetMethod();
                    if (s != null)
                    {
                        methodName = s.Name;
                    }
                }
                LogErrorMessage.getErrormessage(methodName + "方法出现了错误：" + ex.StackTrace);
                Console.WriteLine(methodName + "方法出现了错误：" + ex.StackTrace);
            }
            return list;
        }

        /// <summary>
        /// 根据通道名来删除特定列号数据的spike
        /// </summary>
        /// <param name="originalpath"></param>
        /// <param name="cleanpath"></param>
        /// <param name="configuredfolders"></param>
        /// <param name="columnstoreplace"></param>
        /// <returns></returns>
        public static async Task YXHExcuteCsvSpikeatidx(string originalpath, string cleanpath, string[] configuredfolders, string[] columnsname, int blockLengthMean, int blockLengthVariance, double threshold, double ratio, int threadtimes,int neartimes)
        {
            await Task.Run(async () => {
                Directory.CreateDirectory(cleanpath);
                //获得需要合并的源数据日期文件夹列表
                var ds = Tools_Excute_CSVCombine.YXHGetProcessedFolders(originalpath, cleanpath, configuredfolders);
                if (ds != null)
                {
                    foreach (var d in ds)
                    {
                        Stopwatch timer = new Stopwatch();
                        timer.Start();
                        Console.WriteLine("开始删除spike文件夹名为" + d.Name + "的文件，请稍等片刻");
                        var fileinfo = BasicFilesOperation.YXHGetAllextFiles(d.FullName, "*.csv");

                        if (fileinfo.Length == 0 || fileinfo == null)
                        {
                            Console.WriteLine("文件路径可能有错误，无法读取到csv文件！！！");
                        }
                        else
                        {
                            //筛选0kb的csv的数据并转换成txt
                            fileinfo.YXHTransferZeroCsvtoTxt();
                            for (int i = 0; i < fileinfo.Length; i++)
                            {
                                //文件大小不等0则需要clean
                                if (fileinfo[i].Length != 0)
                                {
                                    string resultdirectorypath = Path.Combine(cleanpath, d.Name);
                                    DirectoryInfo root = new DirectoryInfo(resultdirectorypath);
                                    if (!root.Exists)
                                    {
                                        root.Create();
                                    }
                                    string resultpath = Path.Combine(root.FullName, fileinfo[i].Name);
                                    var odlist = await CSVOperator.YXHReadCSV2ListOneTime(fileinfo[i]);//读取csv数据到List<string>,每一行为一个string
                                    //读取csv多个特定列的数据到List<List<double>>>，这是需要去除spike的数据集
                                    var datalists =  CSVOperator.YXHReadColumnsfromCSV2ListV2(fileinfo[i].FullName, columnsname,out List<int> idx);

                                    for (int j = 0; j < idx.Count; j++)
                                    {
                                        //去除spike后的数据
                                        var spikeremovelist = RemoveSpikeProcess.completeSpikeRemovalProcess(datalists[j], blockLengthMean, blockLengthVariance, threshold, ratio, fileinfo[i].Name);
                                        //把去除spike后的数据替换到原数据
                                        await ReplaceColumnInCSV(odlist, idx[j], spikeremovelist);
                                    }
                                    //最后输出CSV                                  
                                    await CSVOperator.YXHSaveCSVfromListOneTimeAsync(resultpath, odlist);
                                }

                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("合并路径有问题，请检查配置文件");
                }



            });
        }
    }
}
